package main

import (
	"bufio"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"regexp"
	"strings"
)

/**
 * @author huanghm
 * @create 2022.1.11
 */

//正则表达式
//var tplRegex = regexp.MustCompile(`\$\{[0-9A-Z_]+:{1}[0-9a-zA-Z_\\/\-:.\p{Han}]+\}`)
//模板匹配正则表达"${KEY:VALUE}"。KEY匹配字符0-9，大写A-Z，下划线_，前后可以有空格；VALUE取非$、{、}三个字符外任意字符
var tplRegex = regexp.MustCompile(`\$\{ *[0-9A-Z_]+ *:{1}[^\$\{\}]+\}`)
//环境变量正则表达式"KEY=VALUE"。KEY匹配字符0-9，大写A-Z，下划线_，前后可以有空格；VALUE取任意字符
var envRegex = regexp.MustCompile(`^ *[0-9A-Z_]+ *={1}.+$`)

func FindAndReplaceEnv(str string) string {
	newstr := str
	founds := tplRegex.FindAllString(str, -1)
	if founds == nil {
		return str
	}
	for _, found := range founds {
		//去掉${}
		kcon := strings.Replace(strings.Replace(found, "${", "", -1), "}", "", -1)
		//分割第一个:
		item := strings.SplitN(kcon, ":", 2)
		idx, value := GetEnvVal(strings.TrimSpace(item[0]), strings.TrimSpace(item[1]))
		newstr = strings.Replace(newstr, found, value, -1)
		if idx == 0 || idx == 1 || idx == 2 {
			fmt.Printf("    ┣━ %d %s  ->  %s\n", idx, found, value)
		}
	}
	return newstr
}

//获取变量值，优先从变量值文件中获取ENV_KEY值，其次从环境变量中获取ENV_KEY值，都不存在替换默认值
func GetEnvVal(key, defaul string) (int, string) {
	envVal := os.Getenv(key)
	mapVal := envMap[key]

	if mapVal != "" {
		return 0, mapVal
	} else if envVal != "" {
		return 1, envVal
	} else {
		return 2, defaul
	}

}

func UpdateFile(filePath string) {
	if !strings.HasSuffix(filePath, ".tpl") {
		fmt.Println(":( Warning! Only Process '.tpl' Suffix File. Illegal File <" + filePath + ">")
		return
	}

	buf, _ := ioutil.ReadFile(filePath)

	fmt.Println("  --- Process File: <" + filePath + ">")
	content := FindAndReplaceEnv(string(buf))
	newFilePath := strings.Replace(filePath, ".tpl", "", 1)
	fmt.Println("  +++ Generate File: <" + newFilePath + ">")

	ioutil.WriteFile(newFilePath, []byte(content), os.FileMode(0644))
}

var envMap = map[string]string{}

func LoadEnv(filePath string) error {
	var flag bool = false
	f, err := os.Open(filePath)
	if err != nil {
		return err
	}
	defer f.Close()
	fmt.Println("Load EnvFile: " + filePath)
	bfRd := bufio.NewReader(f)
	for {
		line, err := bfRd.ReadBytes('\n')

		str := string(line)
		str = strings.TrimSpace(str)
		//注释行跳过
		if strings.HasPrefix(str, "#") {
			continue
		}

		str = strings.Replace(str, "\n", "", -1)
		if envRegex.MatchString(str) {
			item := strings.SplitN(str, "=", 2)
			if len(item) == 2 {
				flag = true
				key := strings.TrimSpace(item[0])
				envMap[key] = strings.TrimSpace(item[1])
				fmt.Printf("  Set Env: %s=%s\n", key, envMap[key])
			}
		}

		if err != nil { //遇到任何Error立即返回，并忽略 EOF Error信息
			if err == io.EOF {
				if !flag {
					fmt.Println(":( Warning! EnvFile Not Have Env Value!")
				}
				return nil
			}
			return err
		}
	}
}

//遍历目录中文件
func WalkDir(filePath string) {
	//	fmt.Println("遍历目录: " + filePath)
	files, err := ioutil.ReadDir(filePath)
	if err != nil {
		fmt.Println(err.Error())
	} else {
		for _, v := range files {
			subpath := filePath + "/" + v.Name()
			if v.IsDir() {
				if strings.HasPrefix(v.Name(), ".") {
					fmt.Printf(":( Warning! Skip Hide Dir <%s> \n", subpath)
				} else {
					WalkDir(subpath)
				}
			} else {
				if strings.HasSuffix(v.Name(), ".tpl") {
					if strings.HasPrefix(v.Name(), ".") {
						fmt.Printf(":( Warning! Skip Hide File <%s> \n", subpath)
					} else if v.Size() <= 1024*512 {
						fmt.Println("@@ " + v.Name())
						UpdateFile(subpath)
					} else {
						fmt.Printf(":( Warning! Skip File <%s> Size is %dK. TplFile Size is limited 512K.\n", subpath, v.Size()/1024)
					}
				}
			}
		}
	}
}

func main() {

	tmp := strings.Split(os.Args[0], "/")
	pname := tmp[len(tmp)-1]

	if len(os.Args) == 1 {
		fmt.Println("使用说明: 替换.tpl后缀的文本文件中 ${ENV_KEY:default value} 形式的值，并生成去掉.tpl文件名的文件。要求，ENV_KEY必须为[0-9A-Z_]字符。")
		fmt.Println("替换规则: 1.如果有输入变量值文件，优先从变量值文件中获取ENV_KEY值，其次从环境变量中获取ENV_KEY值，都不存在替换默认值")
		fmt.Println("         2.如果没有变量值文件，优先从环境变量中获取ENV_KEY值，不存在替换默认值")
		fmt.Println("Usage 1: " + pname + " <TplFile/Dir>")
		fmt.Println("Usage 2: " + pname + " <TplFile/Dir> <EnvFile>")
		fmt.Println("  参数: <TplFile/Dir> 文件时，输入文件必须是.tpl后缀，然后进行替换操作；目录时，递归变量目录中.tpl后缀的文件，并进行替换操作。")
		fmt.Println("  参数: <EnvFile> 变量替换文件，内容格式: ENV_KEY=value")

		return
	}

	if len(os.Args) == 3 {
		envfile := os.Args[2]
		finfo, err := os.Stat(envfile)
		if err != nil {
			fmt.Println(":( Error! Please Input Legal <EnvFile>.")
			fmt.Println("   " + err.Error())
			return
		}
		if finfo.IsDir() {
			fmt.Println(":( Error! Please Input Legal <EnvFile>.")
			return
		}
		if finfo.Size() > 1024*1024 {
			fmt.Printf(":( Error! EnvFile Limit 1MB Size，But File <%s> Size is %dKB. \n", envfile, finfo.Size()/1024)
			return
		}
		LoadEnv(envfile)
	}

	if len(os.Args) >= 2 {
		srcfile := os.Args[1]

		finfo, err := os.Stat(srcfile)
		if err != nil {
			fmt.Println(":( Error! Please Input Legal <TplFile/Dir>.")
			fmt.Println("   " + err.Error())
			return
		}
		if finfo.IsDir() {
			WalkDir(srcfile)
		} else {
			fmt.Println("@@ " + finfo.Name())
			UpdateFile(srcfile)
		}
	}
}
